<?php

namespace Hx\Db;
use Hx\App;

/**
 * PDO数据库类的封装
 * 针对PDO数据库的封装
 * Class PDO
 * @package Hx\Db
 */
class PDO extends Base {

    /**
     * PDO对象
     * @var \PDO
     */
    protected $pdo;

    /**
     * PDO结果集对象
     * @var \PDOStatement|\PDORow
     */
    protected $stmt;

    /**
     * 构造方法
     * @param array $config 配置数组
     */
    public function __construct($config) {
        parent::__construct($config);
        $this->pdo = new \PDO($this->createDsn(), $this->config['user'], $this->config['password'],
            $this->createOption());
    }

    /**
     * 检测驱动是否正确
     * @param string $driver
     */
    protected function checkDriver($driver) {
        if (strtoupper($driver) !== 'PDO') {
            App::error('database engine error');
        }
    }

    /**
     * 生成DSN
     * @return string
     */
    private function createDsn() {
        switch (strtolower($this->config['type'])) {
            case 'sqlite':
                return 'sqlite:' . $this->config['file'];
            case 'mysql':
            default:
                return 'mysql:host=' . $this->config['host'] . ';port=' . $this->config['port'] . ';dbname=' . $this->config['dbname'];
        }
    }

    /**
     * 生成连接选项
     * @return array
     */
    private function createOption() {
        $option = array(
            \PDO::ATTR_ERRMODE            => \PDO::ERRMODE_EXCEPTION,
            \PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_OBJ
        );
        switch (strtolower($this->config['type'])) {
            case 'mysql':
                #mysql最好处理一下连接时的字符串
                $option[\PDO::MYSQL_ATTR_INIT_COMMAND] = 'set names ' . $this->config['charset'];
                break;
        }
        return $option;
    }

    /**
     * 获取当前PDO连接的类型
     * @return string
     */
    public function getType() {
        return strtolower($this->config['type']);
    }

    /**
     * 执行SQL操作
     * @param string $sql
     * @param array $param 绑定参数
     * @return $this
     */
    public function exec($sql, $param = array()) {
        Sql::log($sql);
        if (!$param) {
            #这个对象是直接查询用的，不需要管重复执行的问题
            $this->stmt = $this->pdo->query($sql);
            return $this;
        }
        #有参数了，需要生成PDOStatement
        if (!$this->stmt) {
            #原来没有，实例化新的
            $this->stmt = $this->pdo->prepare($sql);
        } else {
            #原来有，但是预备语句不同，实例化新的
            if ($this->stmt->queryString != $sql) {
                $this->stmt = $this->pdo->prepare($sql);
            } else {
                #关闭上一次未读取完的状态
                $this->stmt->closeCursor();
            }
        }
        $_param = array();
        #处理参数绑定
        foreach ($param as $k => $v) {
            $_param[':' . $k] = $v;
        }
        $this->stmt->execute($_param);
        return $this;
    }

    /**
     * 序列化结果
     * @param bool $all 返回全部
     * @return null|object|array
     */
    public function fetch($all = FALSE) {
        if (!$this->stmt) {
            return NULL;
        }
        if ($all) {
            return $this->stmt->fetchAll();
        }
        return $this->stmt->fetch();
    }

    /**
     * 最新添加的ID
     * @return string
     */
    public function lastId() {
        return $this->pdo->lastInsertId();
    }

    /**
     * 影响的行数
     * @return int
     */
    public function rowCount() {
        return !$this->stmt ? 0 : $this->stmt->rowCount();
    }

    /**
     * 处理字符串
     * @param string $string
     * @return string
     */
    public function quote($string) {
        if (is_null($string)) {
            return "NULL";
        }
        return $this->pdo->quote($string, \PDO::PARAM_STR);
    }

}